use crate::task::*;
use std::ptr;

struct ListNode {
    task: Box<dyn ITask>,
    prev: *mut ListNode,
    next: *mut ListNode,
}

impl ListNode {
    fn new(task: Box<dyn ITask>) -> *mut Self {
        Box::into_raw(Box::new(Self {
            task,
            prev: ptr::null_mut(),
            next: ptr::null_mut(),
        }))
    }
}

pub struct TaskExecutor {
    head: *mut ListNode,
    tail: *mut ListNode,
}

impl TaskExecutor {
    pub fn new() -> Self {
        Self {
            head: ptr::null_mut(),
            tail: ptr::null_mut(),
        }
    }

    pub fn start_task(&mut self, task: Box<dyn ITask>) {
        let mut node = ListNode::new(task);

        assert!(!node.is_null());

        if self.head.is_null() {
            self.head = node;
            self.tail = node;
        } else {
            unsafe {
                (*node).prev = self.tail;
                (*self.tail).next = node;
            }

            self.tail = node;
        }
    }

    fn remove(&mut self, node: *mut ListNode) {
        assert!(!node.is_null());

        unsafe {
            if ptr::eq(node, self.head) && ptr::eq(node, self.tail) {
                self.head = ptr::null_mut();
                self.tail = ptr::null_mut();
            } else if ptr::eq(self.head, node) {
                self.head = (*node).next;
                (*self.head).prev = ptr::null_mut();
            } else if ptr::eq(self.tail, node) {
                self.tail = (*node).prev;
                (*self.tail).next = ptr::null_mut();
            } else {
                (*(*node).prev).next = (*node).next;
                (*(*node).next).prev = (*node).prev;
            }

            ptr::drop_in_place(node);
        }
    }

    pub fn step(&mut self) {
        let mut p = self.head;

        unsafe {
            while !p.is_null() {
                (*p).task.step();

                if (*p).task.is_finished() {
                    let temp = (*p).next;

                    self.remove(p);
                    p = temp;
                } else {
                    p = (*p).next;
                }
            }
        }
    }

    pub fn task_count(&self) -> u32 {
        let mut p = self.head;
        let mut cnt = 0;
        unsafe {
            while !p.is_null() {
                cnt += 1;
                p = (*p).next;
            }
        }
        cnt
    }
}

impl Drop for TaskExecutor {
    fn drop(&mut self) {
        let mut p = self.head;

        unsafe {
            while !p.is_null() {
                let next = (*p).next;

                ptr::drop_in_place(p);

                p = next;
            }
        }
    }
}